#include "ChildProcessRecorder.h"
#include <windows.h>
#include <sstream>
#include <cassert>
#include <boost/weak_ptr.hpp>
#include <boost/shared_ptr.hpp>
#include "Console.h"

namespace Con
{
	namespace detail
	{
		// RAII wrapper around critical section locking
		struct CSectionLock
		{
			// non-copyable
		private:
			CSectionLock& operator=(const CSectionLock& lock);
			CSectionLock(const CSectionLock&);
			CRITICAL_SECTION& cSec;

		public:
			CSectionLock(CRITICAL_SECTION& sec) : cSec(sec)
			{
				EnterCriticalSection(&cSec);
			}

			~CSectionLock()
			{
				LeaveCriticalSection(&cSec);
			}
		};

		// forward declare exception handler function
		LONG WINAPI ExceptionHandler(LPEXCEPTION_POINTERS);

		//
		// ChildProcessRecorder function definitions
		//
		ChildProcessRecorder::ChildProcessRecorder()
		{
			InitializeCriticalSection(&cSection);
			// add exception handler so that we can shutdown any console owning processes
			// should the app crash
			exceptToken = AddVectoredExceptionHandler(0, &Con::detail::ExceptionHandler);
		}

		ChildProcessRecorder::~ChildProcessRecorder()
		{
			Shutdown();
		}

		// shutdown the console owning processes
		void ChildProcessRecorder::Shutdown()
		{
			// extra scope so we release the lock before deleting it
			{
				CSectionLock lock(cSection);
				Consoles::iterator iter = activeConsoles.begin();
				while((iter = activeConsoles.begin()) != activeConsoles.end())
				{
					Remove(iter->first);
				}
				activeConsoles.clear();
			}
			DeleteCriticalSection(&cSection);
		}

		// add the console to the map
		void ChildProcessRecorder::Add(const Handle& console)
		{
			CSectionLock lock(cSection);
			activeConsoles[console->get_id()] = ConsoleImplHandle(console);
		}

		// Remove the console with the specified ID and set it's exit event
		void ChildProcessRecorder::Remove(DWORD id)
		{
			{
				CSectionLock lock(cSection);
				Consoles::iterator iter = activeConsoles.find(id);
				assert((iter != activeConsoles.end()) && "Tried to remove invalid console");
				activeConsoles.erase(iter);
			}
			std::wostringstream childPidEventName;
			childPidEventName << L"pid" << id;
			HANDLE hExitEvent = OpenEvent(EVENT_MODIFY_STATE, FALSE, childPidEventName.str().c_str());
			if(hExitEvent)
			{
				SetEvent(hExitEvent);
				CloseHandle(hExitEvent);
			}
		}

		// get a handle to the console represented by 'id'
		bool ChildProcessRecorder::Get(DWORD id, Handle& out)
		{
			CSectionLock lock(cSection);
			Consoles::const_iterator iter = activeConsoles.find(id);
			bool ret = (iter != activeConsoles.end());
			if(ret)
			{
				out = iter->second.lock();
			}
			return ret;
		}
	} // namespace detail
} // namespace Con
